{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "2f25f55f",
   "metadata": {},
   "source": [
    "# 3D Solid Mechanics"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2ad7a13b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from netgen.occ import *\n",
    "from netgen.webgui import Draw as DrawGeo\n",
    "import ngsolve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cf326b31",
   "metadata": {},
   "outputs": [],
   "source": [
    "box = Box((0,0,0), (3,0.6,1))\n",
    "box.faces.name=\"outer\"\n",
    "cyl = sum( [Cylinder((0.5+i,0,0.5), Y, 0.25,0.8) for i in range(3)] )\n",
    "cyl.faces.name=\"cyl\"\n",
    "geo = box-cyl\n",
    "\n",
    "DrawGeo(geo);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d54d7749",
   "metadata": {},
   "source": [
    "find edges between box and cylinder, and build chamfers (requires OCC 7.4 or newer):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "96197254",
   "metadata": {
    "tags": [
     "raises-exception"
    ]
   },
   "outputs": [],
   "source": [
    "cylboxedges = geo.faces[\"outer\"].edges * geo.faces[\"cyl\"].edges\n",
    "cylboxedges.name = \"cylbox\"\n",
    "geo = geo.MakeChamfer(cylboxedges, 0.03)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8f356223",
   "metadata": {},
   "source": [
    "name faces for boundary conditions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8701d6a2",
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "geo.faces.Min(X).name = \"fix\"\n",
    "geo.faces.Max(X).name = \"force\"\n",
    "\n",
    "DrawGeo(geo);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "93554691",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ngsolve import *\n",
    "from ngsolve.webgui import Draw\n",
    "mesh = Mesh(OCCGeometry(geo).GenerateMesh(maxh=0.1)).Curve(3)\n",
    "Draw (mesh);"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7d07fa0a",
   "metadata": {},
   "source": [
    "## Linear elasticity\n",
    "\n",
    "Displacement: $u : \\Omega \\rightarrow R^3$\n",
    "\n",
    "Linear strain:\n",
    "$$\n",
    "\\varepsilon(u) := \\tfrac{1}{2} ( \\nabla u + (\\nabla u)^T )\n",
    "$$\n",
    "\n",
    "Stress by Hooke's law:\n",
    "$$\n",
    "\\sigma = 2 \\mu \\varepsilon + \\lambda \\operatorname{tr} \\varepsilon I\n",
    "$$\n",
    "\n",
    "Equilibrium of forces:\n",
    "$$\n",
    "\\operatorname{div} \\sigma = f\n",
    "$$\n",
    "\n",
    "Displacement boundary conditions:\n",
    "$$\n",
    "u = u_D \\qquad \\text{on} \\, \\Gamma_D\n",
    "$$\n",
    "\n",
    "Traction boundary conditions:\n",
    "$$\n",
    "\\sigma n = g \\qquad \\text{on} \\, \\Gamma_N\n",
    "$$\n",
    "\n",
    "\n",
    "Variational formulation:\n",
    "--- \n",
    "Find: $u \\in H^1(\\Omega)^3$ such that $u = u_D$ on $\\Gamma_D$\n",
    "$$\n",
    "\\int_\\Omega \\sigma(\\varepsilon(u)) : \\varepsilon(v) \\, dx = \\int_\\Omega f v dx + \\int_{\\Gamma_N} g v ds\n",
    "$$\n",
    "holds for all $v = 0$ on $\\Gamma_D$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "957549ab",
   "metadata": {},
   "outputs": [],
   "source": [
    "E, nu = 210, 0.2\n",
    "mu  = E / 2 / (1+nu)\n",
    "lam = E * nu / ((1+nu)*(1-2*nu))\n",
    "\n",
    "def Stress(strain):\n",
    "    return 2*mu*strain + lam*Trace(strain)*Id(3)    "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "00178f2a",
   "metadata": {},
   "outputs": [],
   "source": [
    "fes = VectorH1(mesh, order=3, dirichlet=\"fix\")\n",
    "u,v = fes.TnT()\n",
    "gfu = GridFunction(fes)\n",
    "\n",
    "with TaskManager():\n",
    "    a = BilinearForm(InnerProduct(Stress(Sym(Grad(u))), Sym(Grad(v))).Compile()*dx)\n",
    "    pre = Preconditioner(a, \"bddc\")\n",
    "    a.Assemble()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "54b8ead2",
   "metadata": {},
   "outputs": [],
   "source": [
    "force = CF( (1e-3,0,0) )\n",
    "f = LinearForm(force*v*ds(\"force\")).Assemble()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4e0ec2a2",
   "metadata": {},
   "outputs": [],
   "source": [
    "from ngsolve.krylovspace import CGSolver\n",
    "inv = CGSolver(a.mat, pre, printrates='\\r', tol=1e-8)\n",
    "gfu.vec.data = inv * f.vec"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c0845659",
   "metadata": {},
   "outputs": [],
   "source": [
    "with TaskManager():\n",
    "    fesstress = MatrixValued(H1(mesh,order=3), symmetric=True)\n",
    "    gfstress = GridFunction(fesstress)\n",
    "    gfstress.Interpolate (Stress(Sym(Grad(gfu))))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c889b0da",
   "metadata": {},
   "outputs": [],
   "source": [
    "Draw (5e4*gfu, mesh);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c49a4f27",
   "metadata": {},
   "outputs": [],
   "source": [
    "Draw (Norm(gfstress), mesh, deformation=1e4*gfu, draw_vol=False, order=3);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "876acb13",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
